René Nyffenegger's collection of things on the web
René Nyffenegger on Oracle - Most wanted - Feedback -
 

JavaScript Menu

I have been experimenting with JavaScript in order to have a JavaScript Menu. The goal was not a cross browser Menu, but a Menu that I can use to study the nessessary things.
A lot was inspired by http://www.cfoster.net/tmenu.

The JavaScript functions (basic_menu.js)

var root_elem;

var g_menu_item_border_size      =   1;
var g_menu_item_height_wo_border =  15;

var menu_bar_counter   =   0;

var id_counter         =   0;
var all_objs           =   {};

function getBrowserWidth() {return root_elem.clientWidth;  }
function getBrowserHeight(){return root_elem.clientHeight; }
function getScrollY()      {return root_elem.scrollTop;    }
function getScrollX()      {return root_elem.scrollLeft;   }

function getX(obj)         {return obj.offsetLeft + layout.getLeft(obj);      }
function getY(obj)         {return layout.getTop(obj) + layout.getHeight(obj);}

function Menu(label) {
  id_counter++;
  this.id               =               id_counter;
  this.id_label         = "label_id_" + id_counter;
  all_objs[id_counter]  =                     this;

  this.menu_items       =                       [];
  this.ids_of_items     =                       [];

  this.menu_item        = new MenuItem(label, this.id, this.id_label, this);

  this.is_menu          =                     true;
}

function MenuItem(label, id_menu, id, menu) {
  this.label   =   label;
  this.id_menu = id_menu;
  this.id      =      id;
  this.menu    =    menu;
  all_objs[id] =    this;
}

function MenuAction(label, href) {
  id_counter++;
  this.id_label        = "label_id_" + id_counter;
  all_objs[id_counter] =  this;

  this.label           = label;
  this.href            =  href;

  this.is_menu         = false;
}

function test_id(obj, id) {
  // Tests, if obj's id IS id
  // The parameter id is a string
  if (obj            == null)        return false;
  if (typeof(obj   ) == "undefined") return false;
  if (typeof(obj.id) == "undefined") return false;

  if (obj.id==id)  return true;

  return false;
}

Menu.prototype.close = function() {
  document.getElementById(this.id).style.visibility = "hidden";

  var menu_label = document.getElementById(this.id_label);
  menu_label.style.zIndex  = 0;
}

Menu.prototype.open = function() {
  var menu_label = document.getElementById(this.id_label);

  var menu_div = document.getElementById(this.id);

  if (! this.width_calculated) {
    var max_width = 0;
    for (var i=0;i<this.ids_of_items.length;i++) {
      var m = document.getElementById(this.ids_of_items[i])
      if (m.offsetWidth > max_width) max_width = m.offsetWidth;
    }

    menu_div.style.posWidth = max_width+1;
    for (var i=0;i<this.ids_of_items.length;i++) {
      var m = document.getElementById(this.ids_of_items[i])
      m.style.posWidth = max_width-2;
    }

    this.width_calculated = true;
    menu_div.style.height=i*(g_menu_item_height_wo_border+2*g_menu_item_border_size)+2;
    menu_div.style.width =max_width;
  }

  menu_div.style.left = menu_label.clientWidth -20;

  menu_label.style.zIndex  = 1;
  menu_div.style.visibility = "visible";
}

Menu.prototype.mouse_leave = function() {
  for (var i=0;i<this.menu_items.length;i++) {
    if (this.menu_items[i].is_menu) this.menu_items[i].close();
  }

  if (event.toElement == null) return;


  // todo: "label_id_"
  if (! test_id(event.toElement,              "label_id_" + this.id)  &&
      ! test_id(event.toElement.offsetParent, "label_id_" + this.id)) {
     this.close();
  }
}

MenuItem.prototype.mouse_leave = function() {
  var to_elem = event.toElement;
  if (to_elem == null) {
    this.menu.close();
    return;
  }

  if (! test_id(to_elem,              this.id_menu) && 
      ! test_id(to_elem.offsetParent, this.id_menu)) {
    this.menu.close();
    return;
  }
}

MenuItem.prototype.mouse_enter = function() {
  this.menu.open();
}

MenuAction.prototype.a = function(y, x) {
  var ret="";

  ret += "<a id='" + this.id_label + "' ";
  ret += "href='"+this.href+"' ";
  ret += " style='top:" + y + ";left:" + x + "' ";
  ret += "class='MenuItemCl'>";
  ret += this.label;
  ret += "</a>";

  return ret;
}

MenuItem.prototype.a = function(y, x) {
  var ret = "";

  ret += "<a id='" + this.id + "' ";

  ret += "onMouseEnter='javascript:all_objs[\""+this.id+"\"].mouse_enter();'";
  ret += " onMouseLeave='javascript:all_objs[\""+this.id+"\"].mouse_leave();'";
  ret += " style='top:" + y + ";left:" + x + "' ";
  ret += " class='MenuItemCl' href='javascript:void(0)'>";

  ret += this.label;

  ret += this.menu.GetMenuDivTodo();

  ret += "</a>";
  return ret;
}

Menu.prototype.a = function(y, x) {

  var ret="";

  ret += this.menu_item.a(y, x);

  return ret;
 
};

Menu.prototype.GetMenuDivTodo = function() {
  var ret = "<div id='"+this.id+"' class='MenuCl' "

  ret += "onMouseLeave='javascript:all_objs["+this.id+"].mouse_leave();'>";

  var is_first = 1;
  for (var i=0;i<this.menu_items.length;i++) {
    ret += this.menu_items[i].a(i*(g_menu_item_height_wo_border + 2*g_menu_item_border_size), 0);
    is_first = 0;
  }
  ret += "</div>";

  return ret;
}

Menu.prototype.add = function(o) {
  this.menu_items[this.menu_items.length] = o;

  this.ids_of_items[this.ids_of_items.length] = o.id_label;
};

MenuAction.prototype.hide = function() {
  document.getElementById(this.id).style.visibility = "hidden";
}

MenuAction.prototype.show = function() {
  document.getElementById(this.id).style.visibility = "visible";
}

function MenuBar(x,y) {
  // todo: x und y werden nicht beruecksichtigt
  this.id               = menu_bar_counter++;
  this.x                = x;
  this.y                = y;
  this.isAbsolute       = false;
  this.contained_objs   = [];
  this.img_loaded       = false;

  this.width_calculated = false;
}

MenuBar.prototype.add = function(obj) { 
  this.contained_objs[this.contained_objs.length] = obj; 
};

MenuBar.prototype.toString = function() {
  display = "position:absolute;left:"+this.x+"px;top:"+this.y+"px;";

  var ret = "<span id='"+this.id+"' class='MenuBarCl' "+((this.isAbsolute) ? " style='margin:0px;padding:0px;"+display+"'":"");
  
  ret +=">";

  for (var i=0;i<this.contained_objs.length;i++) {
   ret += this.contained_objs[i].a(0, i*120); 
  }
  ret += "</span>";

  return ret;
};

function basic_menu_load() {
  var ieBox = document.compatMode == null || document.compatMode != "CSS1Compat";

  if (ieBox) root_elem = document.body;
  else       root_elem = document.documentElement;
}

The CSS file (basic_menu_1.css)

.MenuBarCl {
  position        :                 absolute;
}

.MenuCl           {
  position        :                 absolute;
  background-color:                  #BED5DC;
  visibility      :                   hidden;
  border          :        1px solid #000000;
  width           :   400;
}

.MenuBarCl
.MenuItemCl       {
  width : 120;
}

.MenuItemCl     {
  position        :                 absolute;
  border          :        1px solid #ECECEC;
  background-color:                  #ECECEC;
  padding         :          1px 5px 0px 7px;
  text-decoration :                     none;
  font-family     : Verdana,Arial,Sans-Serif;
  font-size       :                     12px;
  text-align      :                     left;
  color           :                  #000000;
  margin          :                      0px;
  height          :                     15px;
}

.MenuItemCl:hover {
  background-color:                  #C0C0E0;
  color           :                  #0000FF;
  border          :        1px solid #000000;
  height          :                     15px;
  text-decoration :                     none;
}

.MenuSeparatorCl  {
  font-size       :                      0px;
  background-color:                  #BED5DC;
  height          :                      1px;
  margin          :                      1px;
  border-top      :        1px solid #FFFFFF;
  border-bottom   :        1px solid #FFFFFF;
}

.test_width {
  visibility : hidden;
}



A HTML file using basic_menu.js

<html>

<head>

<title>Basic Javascript Menu</title>

<link   rel ='stylesheet'      type    ='text/css'   href='basic_menu_1.css'>
<script type='text/javascript' language='javascript' src ='basic_menu.js'>    </script>

</head>

<script language=javascript>
var menu_bar          = new MenuBar(0, 0);

var menu_1            = new Menu        ("Menu 1"                       );
var menu_2            = new Menu        ("Menu 2"                       );
var menu_act          = new MenuAction  ("Do!",    "do.html");

var mi_1_one          = new MenuAction  ("Menu 1, one",   "1_one.html"  );
var mi_1_two          = new MenuAction  ("Menu 1, two",   "1_two.html"  );
var mn_1_three        = new Menu        ("Menu 1, three"                );
var mi_1_four         = new MenuAction  ("Menu 1, four",  "1_four.html" );
var mi_1_five         = new MenuAction  ("Menu 1, five",  "1_five.html" );

var mi_2_one          = new MenuAction  ("Menu 2, one",   "2_one.html"  );
var mi_2_two          = new MenuAction  ("Menu 2, two",   "2_two.html"  );
var mi_2_three        = new MenuAction  ("Menu 2, three", "2_three.html");

var mi_1_three_apple  = new MenuAction  ("Apple",         "Apple"       );
var mn_1_three_banana = new Menu        ("Banana"                       );
var mi_1_three_carrot = new MenuAction  ("Carrot",        "Carrot"      );

var mi_banana_green   = new MenuAction  ("Green",         "green.html"  );
var mi_banana_yellow  = new MenuAction  ("Yellow",        "yellow.html" );

mn_1_three_banana.add(mi_banana_green);
mn_1_three_banana.add(mi_banana_yellow);

menu_2.add(mi_2_one  );
menu_2.add(mi_2_two  );
menu_2.add(mi_2_three);

mn_1_three.add(mi_1_three_apple);
mn_1_three.add(mn_1_three_banana);
mn_1_three.add(mi_1_three_carrot);

menu_1.add(mi_1_one  );
menu_1.add(mi_1_two  );
menu_1.add(mn_1_three);
menu_1.add(mi_1_four );
menu_1.add(mi_1_five );

menu_bar.add(menu_1    );
menu_bar.add(menu_2    );
menu_bar.add(menu_act  );

document.write(menu_bar);
</script>

<body onLoad='javascript:basic_menu_load()'>

</body>

</html>


Links